use std::cell::Cell;

use crate::ast::{
  ASTBinaryOperator, ASTBinaryOperatorKind, ASTExpression, ASTStatement, ASTUnaryOperator,
  ASTUnaryOperatorKind, Ast,
};
use crate::diagnostics::DiagnosticBagCell;
use crate::token::{Token, TokenKind};

#[derive(Debug, Clone)]
pub struct Counter {
  value: Cell<usize>,
}

impl Counter {
  pub fn new() -> Self {
    Self {
      value: Cell::new(0),
    }
  }

  pub fn increment(&self) {
    let current_value = self.value.get();
    self.value.set(current_value + 1);
  }

  pub fn get_value(&self) -> usize {
    self.value.get()
  }
}

pub struct Parser {
  tokens: Vec<Token>,
  current: Counter,
  diagnostics_bag: DiagnosticBagCell,
}

impl Parser {
  pub fn new(tokens: Vec<Token>, diagnostics_bag: DiagnosticBagCell) -> Self {
    Self {
      tokens,
      current: Counter::new(),
      diagnostics_bag,
    }
  }

  pub fn print_ast(&mut self) -> Ast {
    let mut ast = Ast::new();

    while let Some(stmt) = self.next_statement() {
      ast.add_statement(stmt);
    }

    ast.visualize();

    ast
  }

  pub fn next_statement(&mut self) -> Option<ASTStatement> {
    if self.is_at_end() {
      return None;
    }
    self.parse_statement()
  }

  fn parse_statement(&mut self) -> Option<ASTStatement> {
    let expr = self.parse_expression()?;

    Some(ASTStatement::expression(expr))
  }

  fn parse_expression(&mut self) -> Option<ASTExpression> {
    self.parse_binary_expression(0)
  }

  fn parse_binary_expression(&mut self, precedence: u8) -> Option<ASTExpression> {
    // let mut lhs = self.parse_primary_expression()?;
    let mut lhs = self.parse_unary_expression()?;

    while let Some(operator) = self.parse_binary_operator() {
      let operator_precedence = operator.precedence();
      // fix: 3 - 2 + 3 / 3 * 1 - 2 / 4 计算出来是 0.5 错误的结果,应该是计算出是 1.5
      if operator_precedence <= precedence {
        break;
      }

      self.consume_token();
      let rhs = self.parse_binary_expression(operator_precedence)?;
      lhs = ASTExpression::binary(operator, lhs, rhs);
    }

    Some(lhs)
  }

  fn parse_unary_expression(&mut self) -> Option<ASTExpression> {
    if let Some(operator) = self.parse_unary_opreator() {
      self.consume_token();
      // 获取到操作数
      let opreand = self.parse_unary_expression().unwrap();
      // 传递负号和操作数
      return Some(ASTExpression::unary(operator, opreand));
    }
    self.parse_primary_expression()
  }

  fn parse_unary_opreator(&mut self) -> Option<ASTUnaryOperator> {
    let token = self.current_token();

    let kind = match token.kind {
      TokenKind::Minus => Some(ASTUnaryOperatorKind::Minus), // 负号 -
      TokenKind::Bang => Some(ASTUnaryOperatorKind::BitwiseNot), // 非 !

      _ => None,
    };

    kind.map(|kind| ASTUnaryOperator::new(kind, token.clone()))
  }

  fn parse_binary_operator(&mut self) -> Option<ASTBinaryOperator> {
    let token = self.current_token();
    let kind = match token.kind {
      TokenKind::Plus => Some(ASTBinaryOperatorKind::Plus),
      TokenKind::Minus => Some(ASTBinaryOperatorKind::Minus),
      TokenKind::Asterisk => Some(ASTBinaryOperatorKind::Multiply),
      TokenKind::Slash => Some(ASTBinaryOperatorKind::Divide),
      _ => None,
    };

    kind.map(|kind| ASTBinaryOperator::new(kind, token.clone()))
  }

  fn parse_primary_expression(&mut self) -> Option<ASTExpression> {
    let token = self.consume_token();

    let ast_expression = match token.kind {
      TokenKind::Number(number) => {
        // token.span.literal.parse::<f64>().unwrap();
        ASTExpression::number(number)
      }
      TokenKind::LeftParen => {
        let expr = self.parse_expression()?;
        // let token = self.consume_token();
        // if token.kind != TokenKind::RightParen {
        //   panic!("Expected right paren");
        // }
        self.consume_and_check(TokenKind::RightParen);
        ASTExpression::parenthesized(expr)
      }
      TokenKind::True | TokenKind::False => {
        let value = token.kind == TokenKind::True;
        ASTExpression::boolean(token.clone(), value)
      }
      _ => {
        self
          .diagnostics_bag
          .borrow_mut()
          .report_expected_expression(token);
        ASTExpression::error(token.span.clone())
      }
    };

    Some(ast_expression)
  }

  fn peek_token(&self, offset: isize) -> &Token {
    let mut index = (self.current.get_value() as isize + offset) as usize;
    if index >= self.tokens.len() {
      index = self.tokens.len() - 1;
    }
    self.tokens.get(index).expect("No exit Token")
  }

  fn current_token(&self) -> &Token {
    self.peek_token(0)
  }

  fn consume_token(&self) -> &Token {
    self.current.increment();
    self.peek_token(-1)
  }

  fn consume_and_check(&self, kind: TokenKind) -> &Token {
    let token = self.consume_token();
    if token.kind != kind {
      self
        .diagnostics_bag
        .borrow_mut()
        .report_unexpected_token(&kind, token)
    }
    token
  }

  fn is_at_end(&self) -> bool {
    self.current_token().kind == TokenKind::Eof
  }
}
